package core.net;

import java.net.*;
import java.io.*;
import java.util.*;
import core.domein.*;

/**
 * Deze klasse handelt een connectie via het UDP protocol af
 * @author Vanpoucke Sven
 *
 */
public class UDPConnection implements Runnable
{
	private int sentPort;
	private int packetsize;
	private DatagramSocket socket;
	private ArrayList<UDPDataListener> listeners = new ArrayList<UDPDataListener>();
	
	/**
	 * Constructor
	 * @param sentPort Poort waarop moet worden verzonden
	 * @param receivePort Poort waarop moet worden ontvangen
	 * @param packetsize Grootte van de packets
	 */
	public UDPConnection(int sentPort, int receivePort, int packetsize)
	{
		this.sentPort = sentPort;
		this.packetsize = packetsize;
		
		try
		{
			socket = new DatagramSocket(receivePort);
		}
		catch(SocketException se)
		{
			se.printStackTrace();
		}
	}
	
	/**
	 * UDPDataListener toevoegen 
	 * @param listener UDPDataListener die moet worden toegevoegd
	 */
	public void addUDPDataListener(UDPDataListener listener)
	{
		listeners.add(listener);
	}
	
	/**
	 * Alle listeners verwittigen dat er data is ontvangen
	 * @param data
	 */
	public void notifyDataReceived(Object data)
	{
		for(UDPDataListener listener:listeners)
		{
			listener.dataReceived(data);
		}
	}
	
	/**
	 * Methode luistert naar de connectie achter een nieuw DataGramPacket en verwerkt de ontvangen data
	 */
	public void run()
	{
		Object input = null;
		DatagramPacket receivedPacket = null;
		do
		{
			try
			{
				byte data[] = new byte[packetsize];
				receivedPacket = new DatagramPacket(data, data.length);
				socket.receive(receivedPacket);
				input = getObjectFromBytes(receivedPacket.getData());
				notifyDataReceived(input);
			}
			catch(IOException ioe)
			{
				ioe.printStackTrace();
			}
		}while(!input.toString().toLowerCase().equals("exit"));
		
		if(!socket.isClosed()) sendPacket("exit", receivedPacket.getAddress());
		
		socket.close();
	}
	
	/**
	 * Verstuurt een object via de connectie
	 * @param packetData Object dat moet worden verstuurd
	 * @param address Address van ontvanger
	 */
	public void sendPacket(Object packetData, InetAddress address)
	{
		byte data[] = getBytesFromObject(packetData);
		try
		{
			DatagramPacket sendPacket = new DatagramPacket(data, data.length, address, sentPort);
			socket.send(sendPacket);
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
	
	/**
	 * Hulpmethode voor het omzetten van een object naar bytes
	 * @param obj Object dat moet worden omgezet
	 * @return Array van bytes 
	 */
	public byte[] getBytesFromObject(Object obj)
	{
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos;
		try
		{
			oos = new ObjectOutputStream(bos);
			oos.writeObject(obj);
		    oos.flush();
		    oos.close();
		    bos.close();
		} catch (IOException e)
		{
			e.printStackTrace();
		}
		byte [] data = bos.toByteArray();
		return data;
	}
	
	/**
	 * Hulpmethode voor het omzetten van een array van bytes naar een object
	 * @param bytes Array van bytes 
	 * @return Object
	 */
	public Object getObjectFromBytes(byte[] bytes)
	{
		Object o = null;
		ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
		try
		{
			ObjectInputStream ois = new ObjectInputStream(bis);
			o = ois.readObject();
		} catch (IOException e)
		{
			e.printStackTrace();
		} catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		return o;
	}
	
	/**
	 * Nagaan of de socket nog actief is of niet
	 * @return True indien de socket niet meer actief is
	 */
	public boolean isClosed()
	{
		if(socket == null) return true;
		
		return socket.isClosed();
	}

}
